package com.github.sbb.sherry.primitives.fetcher.http;

import com.alibaba.fastjson.JSON;
import com.github.sbb.sherry.common.base.Preconditions;
import com.github.sbb.sherry.common.base.ValidateResult;
import com.github.sbb.sherry.common.collect.CollectionUtil;
import com.github.sbb.sherry.common.primitives.StringUtil;
import com.github.sbb.sherry.primitives.config.datasource.DataSource;
import com.github.sbb.sherry.primitives.config.datasource.DataSourceQueryException;
import com.github.sbb.sherry.primitives.config.datasource.HttpDataSourceConfig;
import com.github.sbb.sherry.primitives.config.fetcher.HttpAuth;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.ConnectionPool;
import okhttp3.Credentials;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;


/**
 * HTTP 数据源
 */
public class HttpDataSource implements DataSource {

  private OkHttpClient httpClient;

  /**
   * Instantiates a new Http data source.
   *
   * @param httpDataSourceConfig the http data source config
   */
  public HttpDataSource(HttpDataSourceConfig httpDataSourceConfig) {
    ValidateResult validate = httpDataSourceConfig.validate();
    Preconditions.checkArgument(validate.isPass(), validate.getMessage());

    ConnectionPool connectionPool = new ConnectionPool(httpDataSourceConfig.getMaximumPoolSize(),
        httpDataSourceConfig.getKeepAliveDuration(), TimeUnit.SECONDS);

    httpClient = new OkHttpClient()
        .newBuilder()
        .connectionPool(connectionPool)
        .build();
  }

  /**
   * @param url     the url
   * @param method  the method 创建请求时使用的方法, 作为数据源只支持 GET 和 POST 方法
   * @param headers 自定义请求头
   * @param params  URL 参数
   * @param data    data 是作为请求体被发送的数据,仅适用 POST 请求方法
   * @param auth    HTTP Basic Auth
   * @return the string
   */
  public String executeCall(final String url,
      final String method,
      Map<String, String> headers,
      Map<String, String> params,
      Map<String, String> data,
      HttpAuth auth) throws DataSourceQueryException {

    // 拼接 URL
    String requestUrl = url;

    if (CollectionUtil.isNotEmpty(params)) {
      StringBuilder requestUrlSB = new StringBuilder(requestUrl).append("?");
      for (Entry<String, String> entry : params.entrySet()) {
        requestUrlSB.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
      }
      requestUrl = requestUrlSB.toString();
    }

    Builder builder = new Builder().url(requestUrl);

    if (auth != null) {
      final String credential = Credentials.basic(auth.getUsername(), auth.getPassword());
      builder.header("Authorization", credential);
    }

    String contentType = null;
    if (CollectionUtil.isNotEmpty(headers)) {
      headers.forEach(builder::header);
      contentType = headers.get("Content-Type");
    }
    if (method.equals("GET")) {
      builder.get();
    } else if (method.equals("POST")) {
      if (contentType != null && contentType.contains("application/json")) {
        // POST JSON
        String json = "{}";
        if (CollectionUtil.isNotEmpty(data)) {
          json = JSON.toJSONString(data);
        }
        RequestBody body = RequestBody.create(json, MediaType.parse(contentType));
        builder.post(body);
      } else {
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        FormBody body = formBodyBuilder.build();
        builder.post(body);
      }
    } else {
      throw new DataSourceQueryException(StringUtil.lenientFormat("HTTP 请求 {} 不支持", method));
    }

    Request request = builder.build();
    Call call = httpClient.newCall(request);
    try {
      Response response = call.execute();
      if (response.isSuccessful()) {
        return response.body().string();
      } else {
        throw new DataSourceQueryException(response.message());
      }
    } catch (Exception e) {
      throw new DataSourceQueryException(e);
    }
  }

  @Override
  public void check() {
    Preconditions.checkNotNull(httpClient, "HttpDataSource check 失败");
  }

  @Override
  public void close() {
    if (httpClient != null) {
      httpClient = null;
    }
  }
}
